The UN speech that US president Donald Trump gave to the United Nations General Assembly on Sept. 19, 2017 was fascinating for several reasons. For me personally, it was interesting because the speech included complex sentences, and the content was much more complex than all previous talks that I’d heard from him. Several countries were mentioned in the talk, in different emotional contexts.

So I started to wondered which countries were mentioned in

However,

http://i0.kym-cdn.com/photos/images/newsfeed/000/519/726/2bf.gif

library(magrittr)
library(tidyverse)
library(rvest)
library(tidytext)
library(forcats)
library(wordcloud)
library(wordcloud2)
library(rworldmap)
library(stringr)
library(ggrepel)
library(hunspell)
library(igraph)
library(ggraph)
url <- "http://www.politico.com/story/2017/09/19/trump-un-speech-2017-full-text-transcript-242879"
speech_excerpt <- 
        read_html(url) %>% # Download whole homepage
        html_nodes("style~ p+ p , .lazy-load-slot+ p , .fixed-story-third-paragraph+ p , .story-related+ p , p~ p+ p") %>% # Select the required elements (by css selector)
        html_text() %>% # Make it text
        .[-2] %>% # Remove some random homepage text
        gsub("Mr.", "Mr", ., fixed = T) %>% # Make sure that dots in the text will not signify sentences
        gsub("United States of America", "usa", .) %>% # USA has to be preserved as one expression
        gsub("United States", "usa", .) %>% 
        gsub("America", "usa", .) %>% 
        gsub("States", "usa", .) %>% 
        gsub("North Korea", "north_korea", .) %>% # North Korea should be preserved as one word for now
        data_frame(paragraph = .)
                        
speech_sentences <- 
        speech_excerpt %>% 
        unnest_tokens(sentence, paragraph, token = "sentences")
        
speech_words <- 
        speech_excerpt %>% 
        unnest_tokens(word, paragraph, token = "words") %>% 
        mutate(word = gsub("_", " ", word)) %>% 
        # Here comes a nasty manual stemming of country names. Sadly, I failed to get satisfactory results  on country names with standard stemmers (I tried snowballC, hunspell, and textstem). I also tried to create a custom dictionary with added country names to no avail. What am I missing? Anyway, this works.
        mutate(word = word %>% 
                       str_replace_all("'s$","") %>% # Cut 's
                       if_else(. == "iranian", "iran", .) %>% 
                       if_else(. %in% c("usans", "north koreans"), str_replace(., "ns$",""),.) %>% 
                       if_else(. %in% c("usan","syrian","african","cuban","venezuelan"), str_replace(., "n$",""),.)
        )
speech_words %>% 
        anti_join(stop_words, by = "word") %>% 
        count(word, sort = TRUE) %>% 
        wordcloud2()

        # wordcloud2(figPath = "trump.png") # Image downloaded from http://westviewnews.org/wp-content/uploads/2017/01/donald-trump-profile-silhouette-vector-graphic_template_1469623874993O0L.png
# Check emotional words that were uttered at least 3 times
speech_words %>% 
        count(word, sort = TRUE) %>% 
        inner_join(get_sentiments("bing"), by = "word") %>% 
        filter(n > 3) %>% 
        mutate(n = if_else(sentiment == "negative", -n, n)) %>% 
        ggplot() +
                aes(y = n, x = fct_reorder(word, n), fill = sentiment) +
                geom_col() +
                coord_flip() +
                labs(x = "word", y ="Occurance in speech") +
                ggtitle("Most common words in Trump's 17/09/19 UN speech by sentiment")

Comparison cloud of all words

speech_words %>%
        inner_join(get_sentiments("bing"), by = "word") %>% 
        count(word, sentiment, sort = TRUE) %>%
        spread(sentiment, n, fill = 0L) %>%
        as.data.frame() %>% 
        remove_rownames() %>% 
        column_to_rownames("word") %>% 
        comparison.cloud(colors = c("red", "blue"))

Let’s look into specific emotions using the nrc sentiment dictionary

It is also possible to , look at the words associated with distinct emotions in the whole speech.

speech_words %>%
        inner_join(get_sentiments("nrc"), by = "word") %>% # Use distinct emotion dictionary
        filter(!sentiment %in% c("positive","negative")) %>% # Only look for distinct emotions
        group_by(sentiment) %>% 
        count(sentiment, sort = T) %>% 
        ggplot() +
                aes(x = fct_reorder(sentiment %>% str_to_title, -n), y = n, label = n) +
                geom_col() +
                geom_label(vjust = 1) +
                theme_minimal() +
                labs(title = "The occurance of words linked to distinct emotions in the speech", x = "Word", y = "Frequency")

Let’s put the sentiments on a map

First, let’s see which countries were mentioned and how many times. Obviously, the (United States of) America is first! Iran, Venezuela, North Korea were also mentioned more than 5 times.

# Load map database
map_world <- 
        map_data(map="world") %>% 
        mutate(region = region %>% str_to_lower()) # Make country name lower case to match word
# Calculate mentions of a country, and join geodata
trump_countries <-
        speech_words %>% 
        count(word) %>% 
        right_join(map_world, by = c("word" = "region")) %>% # Match country coordinates to speech
        select(region = word, everything())
# Get country names with the middle of the country coordinates
country_names <- 
        trump_countries %>% 
        drop_na(n) %>%
        group_by(region) %>% 
        summarise(lat = mean(lat),
                  long = mean(long))

Map countries with the number of mentions (USA excluded)

How does this look like on a map? We concentrate on non-US countries, as the USA would outweigh all differences between other countries.

trump_countries %>% 
        ggplot() +
        aes(map_id = region, 
            x = long, 
            y = lat, 
            label = paste0(region %>% str_to_title(),": ", n)) +
        geom_map(aes(fill = n %>% log10(.)), 
                 map = trump_countries) +
        geom_label_repel(data = trump_countries %>% 
                                 drop_na(n) %>% 
                                 group_by(region) %>% 
                                 slice(1), 
                         alpha = .75) +
        scale_fill_gradient(low = "lightblue", 
                            high = "darkblue", 
                            na.value = "grey90") +
        labs(title = "Number of mentions by country", 
             x = "Longitude", 
             y = "Latitude") +
        theme_minimal() +
        theme(legend.position = "none")

# Sentiment of each sentence
sentence_sentiment <-
speech_sentences %>% 
        mutate(sentence_num = row_number(),
               sentence_length = length(sentence)
        ) %>% 
        unnest_tokens(word, sentence, "words") %>% 
        mutate(word = gsub("_", " ", word)) %>% 
        # Here comes a nasty manual stemming of country names. Sadly, I failed to get satisfactory results  on country names with standard stemmers (I tried snowballC, hunspell, and textstem). I also tried to create a custom dictionary with added country names to no avail. What am I missing? Anyway, this works.        
        mutate(word = word %>% 
                       str_replace_all("'s$","") %>% # Cut 's
                       if_else(. == "iranian", "iran", .) %>% 
                       if_else(. %in% c("usans", "north koreans"), str_replace(., "ns$",""),.) %>% 
                       if_else(. %in% c("usan","syrian","african","cuban","venezuelan"), str_replace(., "n$",""),.)
        ) %>% 
        left_join(get_sentiments("bing"), by = "word") %>%
        mutate(sentiment_score = case_when(sentiment == "positive" ~ 1,
                                           sentiment == "negative" ~ -1,
                                           is.na(sentiment) ~ NA_real_)) %>%
        group_by(sentence_num) %>%
        summarise(sum_sentiment = sum(sentiment_score, na.rm = T),
                  sentence = paste(word, collapse = " "))
# Which sentence has a country name
country_sentence <- 
        speech_sentences %>% 
        mutate(sentence_num = row_number()) %>% 
        unnest_tokens(word, sentence, "words") %>% 
        mutate(word = gsub("_", " ", word)) %>% 
        right_join(country_names %>% select(region), by = c("word" = "region")) %>% 
        arrange(sentence_num)
# Sentiment for each country
country_sentiment <-         
        sentence_sentiment %>% 
        full_join(country_sentence, by = "sentence_num") %>% 
        select(region = word, sum_sentiment) %>% 
        drop_na() %>% 
        group_by(region) %>% 
        summarise(country_sentiment = sum(sum_sentiment, na.rm = T))

So, How about looking into the text and and check

sentence_sentiment %>% 
        full_join(country_sentence) %>% 
        mutate(sentiment_type = case_when(sum_sentiment >0 ~ "positive",
                                          sum_sentiment <0 ~ "negative",
                                          sum_sentiment == 0 ~ "neutral") %>% 
                                fct_rev()
                       ) %>% 
        
        ggplot() +
                aes(x = sentence_num, 
                    y = sum_sentiment, 
                    label = word %>% str_to_title()) +
                geom_hline(yintercept = 0, 
                           color = "grey", 
                           linetype = "dashed", 
                           size = 1.2) +
                geom_smooth(span = 0.05, 
                            se = T, 
                            size = 1.2, 
                            color = "black") +
                geom_label_repel(aes(fill = sentiment_type), 
                                 alpha = .8, 
                                 segment.alpha = 0) +
                scale_fill_manual(values = c("green","grey","red")) +
                theme_minimal() +
                labs(x = "Sentence number", 
                     y = "Sentence sentiment", 
                     title = "The summarised sentiment of sentences, and the appearance of country names in the speech \nby sentiment in sentence order",
                     subtitle = "The dashed line signifies neutral sentence sentiment. \nCountry label colors show the direction of the sentiment (positive/negative)") 

sentiment_map_data <- 
        trump_countries %>% 
        left_join(country_sentiment, by = "region")
sentiment_map_data %>% 
        mutate(country_sentiment = if_else(region == "usa", NA_real_, country_sentiment)) %>% # Exclude US
        ggplot() +
                aes(    map_id = region, 
                        x = long, 
                        y = lat, 
                        label = paste0(region %>% str_to_title(), ": ", country_sentiment)
                        ) +
                geom_map(aes(fill = country_sentiment), 
                         map = trump_countries) +
                scale_fill_gradient(high = "green", 
                                    low = "red", 
                                    na.value = "grey90") +
                geom_label_repel(data = sentiment_map_data %>%
                                         drop_na(n) %>%
                                         group_by(region) %>%
                                         slice(1),
                                         alpha = .5
                                 ) +
                theme_minimal() +
                labs(title = "Sentiment of the sentences where countries were mentioned (USA excluded)", 
                     x = "Longitude", 
                     y = "Latitude")

LS0tDQp0aXRsZTogIklzIHRoaXMgdGhlIGJlZ2lubmluZyBvZiB0aGUgZW5kPyAtIFRleHQgbWluaW5nIERvbmFsZCBUcnVtcHMgVU4gc3BlZWNoIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KVGhlIFVOIHNwZWVjaCB0aGF0IFVTIHByZXNpZGVudCBEb25hbGQgVHJ1bXAgZ2F2ZSB0byB0aGUgVW5pdGVkIE5hdGlvbnMgR2VuZXJhbCBBc3NlbWJseSBvbiBTZXB0LiAxOSwgMjAxNyB3YXMgZmFzY2luYXRpbmcgZm9yIHNldmVyYWwgcmVhc29ucy4gRm9yIG1lIHBlcnNvbmFsbHksIGl0IHdhcyBpbnRlcmVzdGluZyBiZWNhdXNlIHRoZSBzcGVlY2ggaW5jbHVkZWQgY29tcGxleCBzZW50ZW5jZXMsIGFuZCB0aGUgY29udGVudCB3YXMgbXVjaCBtb3JlIGNvbXBsZXggdGhhbiBhbGwgcHJldmlvdXMgdGFsa3MgdGhhdCBJJ2QgaGVhcmQgZnJvbSBoaW0uIFNldmVyYWwgY291bnRyaWVzIHdlcmUgbWVudGlvbmVkIGluIHRoZSB0YWxrLCBpbiBkaWZmZXJlbnQgZW1vdGlvbmFsIGNvbnRleHRzLiANCg0KU28gSSBzdGFydGVkIHRvIHdvbmRlcmVkIHdoaWNoIGNvdW50cmllcyB3ZXJlIG1lbnRpb25lZCBpbiANCg0KSG93ZXZlciwgDQoNCiFbXShodHRwOi8vaTAua3ltLWNkbi5jb20vcGhvdG9zL2ltYWdlcy9uZXdzZmVlZC8wMDAvNTE5LzcyNi8yYmYuZ2lmKQ0KDQpodHRwOi8vaTAua3ltLWNkbi5jb20vcGhvdG9zL2ltYWdlcy9uZXdzZmVlZC8wMDAvNTE5LzcyNi8yYmYuZ2lmDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KG1hZ3JpdHRyKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHJ2ZXN0KQ0KbGlicmFyeSh0aWR5dGV4dCkNCmxpYnJhcnkoZm9yY2F0cykNCmxpYnJhcnkod29yZGNsb3VkKQ0KbGlicmFyeSh3b3JkY2xvdWQyKQ0KbGlicmFyeShyd29ybGRtYXApDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KGdncmVwZWwpDQpsaWJyYXJ5KGh1bnNwZWxsKQ0KbGlicmFyeShpZ3JhcGgpDQpsaWJyYXJ5KGdncmFwaCkNCmBgYA0KDQpgYGB7cn0NCnVybCA8LSAiaHR0cDovL3d3dy5wb2xpdGljby5jb20vc3RvcnkvMjAxNy8wOS8xOS90cnVtcC11bi1zcGVlY2gtMjAxNy1mdWxsLXRleHQtdHJhbnNjcmlwdC0yNDI4NzkiDQpzcGVlY2hfZXhjZXJwdCA8LSANCiAgICAgICAgcmVhZF9odG1sKHVybCkgJT4lICMgRG93bmxvYWQgd2hvbGUgaG9tZXBhZ2UNCiAgICAgICAgaHRtbF9ub2Rlcygic3R5bGV+IHArIHAgLCAubGF6eS1sb2FkLXNsb3QrIHAgLCAuZml4ZWQtc3RvcnktdGhpcmQtcGFyYWdyYXBoKyBwICwgLnN0b3J5LXJlbGF0ZWQrIHAgLCBwfiBwKyBwIikgJT4lICMgU2VsZWN0IHRoZSByZXF1aXJlZCBlbGVtZW50cyAoYnkgY3NzIHNlbGVjdG9yKQ0KICAgICAgICBodG1sX3RleHQoKSAlPiUgIyBNYWtlIGl0IHRleHQNCiAgICAgICAgLlstMl0gJT4lICMgUmVtb3ZlIHNvbWUgcmFuZG9tIGhvbWVwYWdlIHRleHQNCiAgICAgICAgZ3N1YigiTXIuIiwgIk1yIiwgLiwgZml4ZWQgPSBUKSAlPiUgIyBNYWtlIHN1cmUgdGhhdCBkb3RzIGluIHRoZSB0ZXh0IHdpbGwgbm90IHNpZ25pZnkgc2VudGVuY2VzDQogICAgICAgIGdzdWIoIlVuaXRlZCBTdGF0ZXMgb2YgQW1lcmljYSIsICJ1c2EiLCAuKSAlPiUgIyBVU0EgaGFzIHRvIGJlIHByZXNlcnZlZCBhcyBvbmUgZXhwcmVzc2lvbg0KICAgICAgICBnc3ViKCJVbml0ZWQgU3RhdGVzIiwgInVzYSIsIC4pICU+JSANCiAgICAgICAgZ3N1YigiQW1lcmljYSIsICJ1c2EiLCAuKSAlPiUgDQogICAgICAgIGdzdWIoIlN0YXRlcyIsICJ1c2EiLCAuKSAlPiUgDQogICAgICAgIGdzdWIoIk5vcnRoIEtvcmVhIiwgIm5vcnRoX2tvcmVhIiwgLikgJT4lICMgTm9ydGggS29yZWEgc2hvdWxkIGJlIHByZXNlcnZlZCBhcyBvbmUgd29yZCBmb3Igbm93DQogICAgICAgIGRhdGFfZnJhbWUocGFyYWdyYXBoID0gLikNCiAgICAgICAgICAgICAgICAgICAgICAgIA0Kc3BlZWNoX3NlbnRlbmNlcyA8LSANCiAgICAgICAgc3BlZWNoX2V4Y2VycHQgJT4lIA0KICAgICAgICB1bm5lc3RfdG9rZW5zKHNlbnRlbmNlLCBwYXJhZ3JhcGgsIHRva2VuID0gInNlbnRlbmNlcyIpDQogICAgICAgIA0Kc3BlZWNoX3dvcmRzIDwtIA0KICAgICAgICBzcGVlY2hfZXhjZXJwdCAlPiUgDQogICAgICAgIHVubmVzdF90b2tlbnMod29yZCwgcGFyYWdyYXBoLCB0b2tlbiA9ICJ3b3JkcyIpICU+JSANCiAgICAgICAgbXV0YXRlKHdvcmQgPSBnc3ViKCJfIiwgIiAiLCB3b3JkKSkgJT4lIA0KICAgICAgICAjIEhlcmUgY29tZXMgYSBuYXN0eSBtYW51YWwgc3RlbW1pbmcgb2YgY291bnRyeSBuYW1lcy4gU2FkbHksIEkgZmFpbGVkIHRvIGdldCBzYXRpc2ZhY3RvcnkgcmVzdWx0cyAgb24gY291bnRyeSBuYW1lcyB3aXRoIHN0YW5kYXJkIHN0ZW1tZXJzIChJIHRyaWVkIHNub3diYWxsQywgaHVuc3BlbGwsIGFuZCB0ZXh0c3RlbSkuIEkgYWxzbyB0cmllZCB0byBjcmVhdGUgYSBjdXN0b20gZGljdGlvbmFyeSB3aXRoIGFkZGVkIGNvdW50cnkgbmFtZXMgdG8gbm8gYXZhaWwuIFdoYXQgYW0gSSBtaXNzaW5nPyBBbnl3YXksIHRoaXMgd29ya3MuDQogICAgICAgIG11dGF0ZSh3b3JkID0gd29yZCAlPiUgDQogICAgICAgICAgICAgICAgICAgICAgIHN0cl9yZXBsYWNlX2FsbCgiJ3MkIiwiIikgJT4lICMgQ3V0ICdzDQogICAgICAgICAgICAgICAgICAgICAgIGlmX2Vsc2UoLiA9PSAiaXJhbmlhbiIsICJpcmFuIiwgLikgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgICBpZl9lbHNlKC4gJWluJSBjKCJ1c2FucyIsICJub3J0aCBrb3JlYW5zIiksIHN0cl9yZXBsYWNlKC4sICJucyQiLCIiKSwuKSAlPiUgDQogICAgICAgICAgICAgICAgICAgICAgIGlmX2Vsc2UoLiAlaW4lIGMoInVzYW4iLCJzeXJpYW4iLCJhZnJpY2FuIiwiY3ViYW4iLCJ2ZW5lenVlbGFuIiksIHN0cl9yZXBsYWNlKC4sICJuJCIsIiIpLC4pDQogICAgICAgICkNCmBgYA0KDQoNCmBgYHtyIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTgsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpzcGVlY2hfd29yZHMgJT4lIA0KICAgICAgICBhbnRpX2pvaW4oc3RvcF93b3JkcywgYnkgPSAid29yZCIpICU+JSANCiAgICAgICAgY291bnQod29yZCwgc29ydCA9IFRSVUUpICU+JSANCiAgICAgICAgd29yZGNsb3VkMigpDQogICAgICAgICMgd29yZGNsb3VkMihmaWdQYXRoID0gInRydW1wLnBuZyIpICMgSW1hZ2UgZG93bmxvYWRlZCBmcm9tIGh0dHA6Ly93ZXN0dmlld25ld3Mub3JnL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE3LzAxL2RvbmFsZC10cnVtcC1wcm9maWxlLXNpbGhvdWV0dGUtdmVjdG9yLWdyYXBoaWNfdGVtcGxhdGVfMTQ2OTYyMzg3NDk5M08wTC5wbmcNCmBgYA0KDQoNCmBgYHtyIGZpZy53aWR0aD0xMH0NCiMgQ2hlY2sgZW1vdGlvbmFsIHdvcmRzIHRoYXQgd2VyZSB1dHRlcmVkIGF0IGxlYXN0IDMgdGltZXMNCnNwZWVjaF93b3JkcyAlPiUgDQogICAgICAgIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKSAlPiUgDQogICAgICAgIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoImJpbmciKSwgYnkgPSAid29yZCIpICU+JSANCiAgICAgICAgZmlsdGVyKG4gPiAzKSAlPiUgDQogICAgICAgIG11dGF0ZShuID0gaWZfZWxzZShzZW50aW1lbnQgPT0gIm5lZ2F0aXZlIiwgLW4sIG4pKSAlPiUgDQogICAgICAgIGdncGxvdCgpICsNCiAgICAgICAgICAgICAgICBhZXMoeSA9IG4sIHggPSBmY3RfcmVvcmRlcih3b3JkLCBuKSwgZmlsbCA9IHNlbnRpbWVudCkgKw0KICAgICAgICAgICAgICAgIGdlb21fY29sKCkgKw0KICAgICAgICAgICAgICAgIGNvb3JkX2ZsaXAoKSArDQogICAgICAgICAgICAgICAgbGFicyh4ID0gIndvcmQiLCANCiAgICAgICAgICAgICAgICAgICAgIHkgPSJPY2N1cmFuY2UgaW4gc3BlZWNoIiwNCiAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gIk1vc3QgY29tbW9uIHdvcmRzIGluIFRydW1wJ3MgMTcvMDkvMTkgVU4gc3BlZWNoIGJ5IHNlbnRpbWVudCIpDQpgYGANCiMgQ29tcGFyaXNvbiBjbG91ZCBvZiBhbGwgd29yZHMNCmBgYHtyIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTh9DQpzcGVlY2hfd29yZHMgJT4lDQogICAgICAgIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoImJpbmciKSwgYnkgPSAid29yZCIpICU+JSANCiAgICAgICAgY291bnQod29yZCwgc2VudGltZW50LCBzb3J0ID0gVFJVRSkgJT4lDQogICAgICAgIHNwcmVhZChzZW50aW1lbnQsIG4sIGZpbGwgPSAwTCkgJT4lDQogICAgICAgIGFzLmRhdGEuZnJhbWUoKSAlPiUgDQogICAgICAgIHJlbW92ZV9yb3duYW1lcygpICU+JSANCiAgICAgICAgY29sdW1uX3RvX3Jvd25hbWVzKCJ3b3JkIikgJT4lIA0KICAgICAgICBjb21wYXJpc29uLmNsb3VkKGNvbG9ycyA9IGMoInJlZCIsICJibHVlIikpDQpgYGANCg0KIyBMZXQncyBsb29rIGludG8gc3BlY2lmaWMgZW1vdGlvbnMgdXNpbmcgdGhlIG5yYyBzZW50aW1lbnQgZGljdGlvbmFyeQ0KDQpJdCBpcyBhbHNvIHBvc3NpYmxlIHRvICwgbG9vayBhdCB0aGUgd29yZHMgYXNzb2NpYXRlZCB3aXRoIGRpc3RpbmN0IGVtb3Rpb25zIGluIHRoZSB3aG9sZSBzcGVlY2guDQoNCmBgYHtyfQ0Kc3BlZWNoX3dvcmRzICU+JQ0KICAgICAgICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJucmMiKSwgYnkgPSAid29yZCIpICU+JSAjIFVzZSBkaXN0aW5jdCBlbW90aW9uIGRpY3Rpb25hcnkNCiAgICAgICAgZmlsdGVyKCFzZW50aW1lbnQgJWluJSBjKCJwb3NpdGl2ZSIsIm5lZ2F0aXZlIikpICU+JSAjIE9ubHkgbG9vayBmb3IgZGlzdGluY3QgZW1vdGlvbnMNCiAgICAgICAgZ3JvdXBfYnkoc2VudGltZW50KSAlPiUgDQogICAgICAgIGNvdW50KHNlbnRpbWVudCwgc29ydCA9IFQpICU+JSANCiAgICAgICAgZ2dwbG90KCkgKw0KICAgICAgICAgICAgICAgIGFlcyh4ID0gZmN0X3Jlb3JkZXIoc2VudGltZW50ICU+JSBzdHJfdG9fdGl0bGUsIC1uKSwgDQogICAgICAgICAgICAgICAgICAgIHkgPSBuLCANCiAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBuKSArDQogICAgICAgICAgICAgICAgZ2VvbV9jb2woKSArDQogICAgICAgICAgICAgICAgZ2VvbV9sYWJlbCh2anVzdCA9IDEpICsNCiAgICAgICAgICAgICAgICB0aGVtZV9taW5pbWFsKCkgKw0KICAgICAgICAgICAgICAgIGxhYnModGl0bGUgPSAiVGhlIG9jY3VyYW5jZSBvZiB3b3JkcyBsaW5rZWQgdG8gZGlzdGluY3QgZW1vdGlvbnMgaW4gdGhlIHNwZWVjaCIsIA0KICAgICAgICAgICAgICAgICAgICAgeCA9ICJXb3JkIiwgDQogICAgICAgICAgICAgICAgICAgICB5ID0gIkZyZXF1ZW5jeSIpDQpgYGANCg0KIyBMZXQncyBwdXQgdGhlIHNlbnRpbWVudHMgb24gYSBtYXANCg0KRmlyc3QsIGxldCdzIHNlZSB3aGljaCBjb3VudHJpZXMgd2VyZSBtZW50aW9uZWQgYW5kIGhvdyBtYW55IHRpbWVzLiBPYnZpb3VzbHksIHRoZSAoVW5pdGVkIFN0YXRlcyBvZikgQW1lcmljYSBpcyBmaXJzdCEgSXJhbiwgVmVuZXp1ZWxhLCBOb3J0aCBLb3JlYSB3ZXJlIGFsc28gbWVudGlvbmVkIG1vcmUgdGhhbiA1IHRpbWVzLg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBMb2FkIG1hcCBkYXRhYmFzZQ0KbWFwX3dvcmxkIDwtIA0KICAgICAgICBtYXBfZGF0YShtYXA9IndvcmxkIikgJT4lIA0KICAgICAgICBtdXRhdGUocmVnaW9uID0gcmVnaW9uICU+JSBzdHJfdG9fbG93ZXIoKSkgIyBNYWtlIGNvdW50cnkgbmFtZSBsb3dlciBjYXNlIHRvIG1hdGNoIHdvcmQNCg0KIyBDYWxjdWxhdGUgbWVudGlvbnMgb2YgYSBjb3VudHJ5LCBhbmQgam9pbiBnZW9kYXRhDQp0cnVtcF9jb3VudHJpZXMgPC0NCiAgICAgICAgc3BlZWNoX3dvcmRzICU+JSANCiAgICAgICAgY291bnQod29yZCkgJT4lIA0KICAgICAgICByaWdodF9qb2luKG1hcF93b3JsZCwgYnkgPSBjKCJ3b3JkIiA9ICJyZWdpb24iKSkgJT4lICMgTWF0Y2ggY291bnRyeSBjb29yZGluYXRlcyB0byBzcGVlY2gNCiAgICAgICAgc2VsZWN0KHJlZ2lvbiA9IHdvcmQsIGV2ZXJ5dGhpbmcoKSkNCg0KIyBHZXQgY291bnRyeSBuYW1lcyB3aXRoIHRoZSBtaWRkbGUgb2YgdGhlIGNvdW50cnkgY29vcmRpbmF0ZXMNCmNvdW50cnlfbmFtZXMgPC0gDQogICAgICAgIHRydW1wX2NvdW50cmllcyAlPiUgDQogICAgICAgIGRyb3BfbmEobikgJT4lDQogICAgICAgIGdyb3VwX2J5KHJlZ2lvbikgJT4lIA0KICAgICAgICBzdW1tYXJpc2UobGF0ID0gbWVhbihsYXQpLA0KICAgICAgICAgICAgICAgICAgbG9uZyA9IG1lYW4obG9uZykpDQoNCmBgYA0KIyBNYXAgY291bnRyaWVzIHdpdGggdGhlIG51bWJlciBvZiBtZW50aW9ucyAoVVNBIGV4Y2x1ZGVkKQ0KDQpIb3cgZG9lcyB0aGlzIGxvb2sgbGlrZSBvbiBhIG1hcD8gV2UgY29uY2VudHJhdGUgb24gbm9uLVVTIGNvdW50cmllcywgYXMgdGhlIFVTQSB3b3VsZCBvdXR3ZWlnaCBhbGwgZGlmZmVyZW5jZXMgYmV0d2VlbiBvdGhlciBjb3VudHJpZXMuDQoNCmBgYHtyIGZpZy53aWR0aD0xMCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnRydW1wX2NvdW50cmllcyAlPiUgDQogICAgICAgIGdncGxvdCgpICsNCiAgICAgICAgYWVzKG1hcF9pZCA9IHJlZ2lvbiwgDQogICAgICAgICAgICB4ID0gbG9uZywgDQogICAgICAgICAgICB5ID0gbGF0LCANCiAgICAgICAgICAgIGxhYmVsID0gcGFzdGUwKHJlZ2lvbiAlPiUgc3RyX3RvX3RpdGxlKCksIjogIiwgbikpICsNCiAgICAgICAgZ2VvbV9tYXAoYWVzKGZpbGwgPSBuICU+JSBsb2cxMCguKSksIA0KICAgICAgICAgICAgICAgICBtYXAgPSB0cnVtcF9jb3VudHJpZXMpICsNCiAgICAgICAgZ2VvbV9sYWJlbF9yZXBlbChkYXRhID0gdHJ1bXBfY291bnRyaWVzICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRyb3BfbmEobikgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBfYnkocmVnaW9uKSAlPiUgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzbGljZSgxKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAuNzUpICsNCiAgICAgICAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAibGlnaHRibHVlIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgaGlnaCA9ICJkYXJrYmx1ZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnZhbHVlID0gImdyZXk5MCIpICsNCiAgICAgICAgbGFicyh0aXRsZSA9ICJOdW1iZXIgb2YgbWVudGlvbnMgYnkgY291bnRyeSIsIA0KICAgICAgICAgICAgIHggPSAiTG9uZ2l0dWRlIiwgDQogICAgICAgICAgICAgeSA9ICJMYXRpdHVkZSIpICsNCiAgICAgICAgdGhlbWVfbWluaW1hbCgpICsNCiAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KDQoNCmBgYA0KDQpgYGB7cn0NCiMgU2VudGltZW50IG9mIGVhY2ggc2VudGVuY2UNCnNlbnRlbmNlX3NlbnRpbWVudCA8LQ0Kc3BlZWNoX3NlbnRlbmNlcyAlPiUgDQogICAgICAgIG11dGF0ZShzZW50ZW5jZV9udW0gPSByb3dfbnVtYmVyKCksDQogICAgICAgICAgICAgICBzZW50ZW5jZV9sZW5ndGggPSBsZW5ndGgoc2VudGVuY2UpDQogICAgICAgICkgJT4lIA0KICAgICAgICB1bm5lc3RfdG9rZW5zKHdvcmQsIHNlbnRlbmNlLCAid29yZHMiKSAlPiUgDQogICAgICAgIG11dGF0ZSh3b3JkID0gZ3N1YigiXyIsICIgIiwgd29yZCkpICU+JSANCiAgICAgICAgIyBIZXJlIGNvbWVzIGEgbmFzdHkgbWFudWFsIHN0ZW1taW5nIG9mIGNvdW50cnkgbmFtZXMuIFNhZGx5LCBJIGZhaWxlZCB0byBnZXQgc2F0aXNmYWN0b3J5IHJlc3VsdHMgIG9uIGNvdW50cnkgbmFtZXMgd2l0aCBzdGFuZGFyZCBzdGVtbWVycyAoSSB0cmllZCBzbm93YmFsbEMsIGh1bnNwZWxsLCBhbmQgdGV4dHN0ZW0pLiBJIGFsc28gdHJpZWQgdG8gY3JlYXRlIGEgY3VzdG9tIGRpY3Rpb25hcnkgd2l0aCBhZGRlZCBjb3VudHJ5IG5hbWVzIHRvIG5vIGF2YWlsLiBXaGF0IGFtIEkgbWlzc2luZz8gQW55d2F5LCB0aGlzIHdvcmtzLiAgICAgICAgDQogICAgICAgIG11dGF0ZSh3b3JkID0gd29yZCAlPiUgDQogICAgICAgICAgICAgICAgICAgICAgIHN0cl9yZXBsYWNlX2FsbCgiJ3MkIiwiIikgJT4lICMgQ3V0ICdzDQogICAgICAgICAgICAgICAgICAgICAgIGlmX2Vsc2UoLiA9PSAiaXJhbmlhbiIsICJpcmFuIiwgLikgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgICBpZl9lbHNlKC4gJWluJSBjKCJ1c2FucyIsICJub3J0aCBrb3JlYW5zIiksIHN0cl9yZXBsYWNlKC4sICJucyQiLCIiKSwuKSAlPiUgDQogICAgICAgICAgICAgICAgICAgICAgIGlmX2Vsc2UoLiAlaW4lIGMoInVzYW4iLCJzeXJpYW4iLCJhZnJpY2FuIiwiY3ViYW4iLCJ2ZW5lenVlbGFuIiksIHN0cl9yZXBsYWNlKC4sICJuJCIsIiIpLC4pDQogICAgICAgICkgJT4lIA0KICAgICAgICBsZWZ0X2pvaW4oZ2V0X3NlbnRpbWVudHMoImJpbmciKSwgYnkgPSAid29yZCIpICU+JQ0KICAgICAgICBtdXRhdGUoc2VudGltZW50X3Njb3JlID0gY2FzZV93aGVuKHNlbnRpbWVudCA9PSAicG9zaXRpdmUiIH4gMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZW50aW1lbnQgPT0gIm5lZ2F0aXZlIiB+IC0xLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlzLm5hKHNlbnRpbWVudCkgfiBOQV9yZWFsXykpICU+JQ0KICAgICAgICBncm91cF9ieShzZW50ZW5jZV9udW0pICU+JQ0KICAgICAgICBzdW1tYXJpc2Uoc3VtX3NlbnRpbWVudCA9IHN1bShzZW50aW1lbnRfc2NvcmUsIG5hLnJtID0gVCksDQogICAgICAgICAgICAgICAgICBzZW50ZW5jZSA9IHBhc3RlKHdvcmQsIGNvbGxhcHNlID0gIiAiKSkNCg0KIyBXaGljaCBzZW50ZW5jZSBoYXMgYSBjb3VudHJ5IG5hbWUNCmNvdW50cnlfc2VudGVuY2UgPC0gDQogICAgICAgIHNwZWVjaF9zZW50ZW5jZXMgJT4lIA0KICAgICAgICBtdXRhdGUoc2VudGVuY2VfbnVtID0gcm93X251bWJlcigpKSAlPiUgDQogICAgICAgIHVubmVzdF90b2tlbnMod29yZCwgc2VudGVuY2UsICJ3b3JkcyIpICU+JSANCiAgICAgICAgbXV0YXRlKHdvcmQgPSBnc3ViKCJfIiwgIiAiLCB3b3JkKSkgJT4lIA0KICAgICAgICByaWdodF9qb2luKGNvdW50cnlfbmFtZXMgJT4lIHNlbGVjdChyZWdpb24pLCBieSA9IGMoIndvcmQiID0gInJlZ2lvbiIpKSAlPiUgDQogICAgICAgIGFycmFuZ2Uoc2VudGVuY2VfbnVtKQ0KDQojIFNlbnRpbWVudCBmb3IgZWFjaCBjb3VudHJ5DQpjb3VudHJ5X3NlbnRpbWVudCA8LSAgICAgICAgIA0KICAgICAgICBzZW50ZW5jZV9zZW50aW1lbnQgJT4lIA0KICAgICAgICBmdWxsX2pvaW4oY291bnRyeV9zZW50ZW5jZSwgYnkgPSAic2VudGVuY2VfbnVtIikgJT4lIA0KICAgICAgICBzZWxlY3QocmVnaW9uID0gd29yZCwgc3VtX3NlbnRpbWVudCkgJT4lIA0KICAgICAgICBkcm9wX25hKCkgJT4lIA0KICAgICAgICBncm91cF9ieShyZWdpb24pICU+JSANCiAgICAgICAgc3VtbWFyaXNlKGNvdW50cnlfc2VudGltZW50ID0gc3VtKHN1bV9zZW50aW1lbnQsIG5hLnJtID0gVCkpDQoNCmBgYA0KDQpTbywgSG93IGFib3V0IGxvb2tpbmcgaW50byB0aGUgdGV4dCBhbmQgYW5kIGNoZWNrIA0KDQpgYGB7ciBmaWcud2lkdGg9MTAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpzZW50ZW5jZV9zZW50aW1lbnQgJT4lIA0KICAgICAgICBmdWxsX2pvaW4oY291bnRyeV9zZW50ZW5jZSkgJT4lIA0KICAgICAgICBtdXRhdGUoc2VudGltZW50X3R5cGUgPSBjYXNlX3doZW4oc3VtX3NlbnRpbWVudCA+MCB+ICJwb3NpdGl2ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW1fc2VudGltZW50IDwwIH4gIm5lZ2F0aXZlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bV9zZW50aW1lbnQgPT0gMCB+ICJuZXV0cmFsIikgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmY3RfcmV2KCkNCiAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgDQogICAgICAgIA0KICAgICAgICBnZ3Bsb3QoKSArDQogICAgICAgICAgICAgICAgYWVzKHggPSBzZW50ZW5jZV9udW0sIA0KICAgICAgICAgICAgICAgICAgICB5ID0gc3VtX3NlbnRpbWVudCwgDQogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gd29yZCAlPiUgc3RyX3RvX3RpdGxlKCkpICsNCiAgICAgICAgICAgICAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImdyZXkiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDEuMikgKw0KICAgICAgICAgICAgICAgIGdlb21fc21vb3RoKHNwYW4gPSAwLjA1LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZSA9IFQsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxLjIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIikgKw0KICAgICAgICAgICAgICAgIGdlb21fbGFiZWxfcmVwZWwoYWVzKGZpbGwgPSBzZW50aW1lbnRfdHlwZSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAuOCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWdtZW50LmFscGhhID0gMCkgKw0KICAgICAgICAgICAgICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImdyZWVuIiwiZ3JleSIsInJlZCIpKSArDQogICAgICAgICAgICAgICAgdGhlbWVfbWluaW1hbCgpICsNCiAgICAgICAgICAgICAgICBsYWJzKHggPSAiU2VudGVuY2UgbnVtYmVyIiwgDQogICAgICAgICAgICAgICAgICAgICB5ID0gIlNlbnRlbmNlIHNlbnRpbWVudCIsIA0KICAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSAiVGhlIHN1bW1hcmlzZWQgc2VudGltZW50IG9mIHNlbnRlbmNlcywgYW5kIHRoZSBhcHBlYXJhbmNlIG9mIGNvdW50cnkgbmFtZXMgaW4gdGhlIHNwZWVjaCBcbmJ5IHNlbnRpbWVudCBpbiBzZW50ZW5jZSBvcmRlciIsDQogICAgICAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9ICJUaGUgZGFzaGVkIGxpbmUgc2lnbmlmaWVzIG5ldXRyYWwgc2VudGVuY2Ugc2VudGltZW50LiBcbkNvdW50cnkgbGFiZWwgY29sb3JzIHNob3cgdGhlIGRpcmVjdGlvbiBvZiB0aGUgc2VudGltZW50IChwb3NpdGl2ZS9uZWdhdGl2ZSkiKSANCg0KYGBgDQoNCg0KYGBge3IgZmlnLndpZHRoPTEwLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Kc2VudGltZW50X21hcF9kYXRhIDwtIA0KICAgICAgICB0cnVtcF9jb3VudHJpZXMgJT4lIA0KICAgICAgICBsZWZ0X2pvaW4oY291bnRyeV9zZW50aW1lbnQsIGJ5ID0gInJlZ2lvbiIpDQoNCnNlbnRpbWVudF9tYXBfZGF0YSAlPiUgDQogICAgICAgIG11dGF0ZShjb3VudHJ5X3NlbnRpbWVudCA9IGlmX2Vsc2UocmVnaW9uID09ICJ1c2EiLCBOQV9yZWFsXywgY291bnRyeV9zZW50aW1lbnQpKSAlPiUgIyBFeGNsdWRlIFVTDQogICAgICAgIGdncGxvdCgpICsNCiAgICAgICAgICAgICAgICBhZXMoICAgIG1hcF9pZCA9IHJlZ2lvbiwgDQogICAgICAgICAgICAgICAgICAgICAgICB4ID0gbG9uZywgDQogICAgICAgICAgICAgICAgICAgICAgICB5ID0gbGF0LCANCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUwKHJlZ2lvbiAlPiUgc3RyX3RvX3RpdGxlKCksICI6ICIsIGNvdW50cnlfc2VudGltZW50KQ0KICAgICAgICAgICAgICAgICAgICAgICAgKSArDQogICAgICAgICAgICAgICAgZ2VvbV9tYXAoYWVzKGZpbGwgPSBjb3VudHJ5X3NlbnRpbWVudCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgIG1hcCA9IHRydW1wX2NvdW50cmllcykgKw0KICAgICAgICAgICAgICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnQoaGlnaCA9ICJncmVlbiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG93ID0gInJlZCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEudmFsdWUgPSAiZ3JleTkwIikgKw0KICAgICAgICAgICAgICAgIGdlb21fbGFiZWxfcmVwZWwoZGF0YSA9IHNlbnRpbWVudF9tYXBfZGF0YSAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHJvcF9uYShuKSAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBfYnkocmVnaW9uKSAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2xpY2UoMSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gLjUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgKw0KICAgICAgICAgICAgICAgIHRoZW1lX21pbmltYWwoKSArDQogICAgICAgICAgICAgICAgbGFicyh0aXRsZSA9ICJTZW50aW1lbnQgb2YgdGhlIHNlbnRlbmNlcyB3aGVyZSBjb3VudHJpZXMgd2VyZSBtZW50aW9uZWQgKFVTQSBleGNsdWRlZCkiLCANCiAgICAgICAgICAgICAgICAgICAgIHggPSAiTG9uZ2l0dWRlIiwgDQogICAgICAgICAgICAgICAgICAgICB5ID0gIkxhdGl0dWRlIikNCg0KYGBgDQoNCg==